Tips&Tricks I trucchi del mestiere

 

Come controllare la porta parallela da qualunque sistema Operativo

Il controllo delle porte I/O Φ un problema delicato, ove il software e l'hardware arrivano a sfiorarsi. ╚ possibile inviare al PC istruzioni maldestre che compromettano il funzionamento della macchina. Per scongiurare eventuali problemi i sistemi operativi della Microsoft dopo NT4 hanno preso il controllo completo delle porte I/O. Le istruzioni di lettura e scrittura come port nel PASCAL e inp nel C generano un errore istruzione privilegiata terminando il flusso del programma.
Per questo motivo negli articoli di elettronica applicata pubblicati da questa rivista si specifica si utilizzare come sistema operativo Windows 98 o 95.



Il problema si risolve utilizzando un driver. Ad esempio si pu≥ sfruttare PortTalk Driver for Windows NT/2000/XP che si scarica da http://www.beyondlogic.org. Questo driver funziona correttamente con i sistemi Windows NT/2000/XP ( in allegato trovate il file porttalk22.zip con la documentazione ufficiale).
Rimane il problema di non conoscere a priori il sistema operativo della macchina. Se abbiamo un sistema operativo fino a Windows ME dobbiamo usare i comandi inp(), altrimenti dobbiamo ricorrere al Driver.

Riconoscere il sistema operativo

Per risolvere questo problema ho scritto una classe Cport oltre incorporare il codice di gestione del driver va a riconoscere il sistema operativo. Questa operazione Φ svolta dalla seguente funzione

BOOL CPort::GetOpSystem()
{
	OSVERSIONINFO *osvi;
	osvi = new(OSVERSIONINFO);
	osvi->dwOSVersionInfoSize=sizeof(OSVERSIONINFO) ;
	GetVersionEx (osvi);
	bIsWindowsNTorLater = (osvi->dwPlatformId == VER_PLATFORM_WIN32_NT );
	try {delete osvi;}
	catch(...){;};
	return bIsWindowsNTorLater;
}

per cui prima di leggere/scrivere il valore della porta si controlla il tipo di sistema operativo

unsigned char CPort::inport(unsigned short PortAddress)
{
    if (!bIsWindowsNTorLater)
	{
		return _inp(PortAddress);
	};

	unsigned int error;
    DWORD BytesReturned;
    unsigned char Buffer[3];
    unsigned short * pBuffer;
    pBuffer = (unsigned short *)&Buffer;
    *pBuffer = PortAddress;

    error = DeviceIoControl(PortTalk_Handle,
                            IOCTL_READ_PORT_UCHAR,
                            &Buffer,
                            2,
                            &Buffer,
                            1,
                            &BytesReturned,
                            NULL);

    if (!error)
		AfxMessageBox("Errore di comunicazione con il driver",MB_OK,0);
    return(Buffer[0]);

}


void CPort::outport(unsigned short PortAddress, unsigned char byte)
{
	if (!bIsWindowsNTorLater)
	{
		_outp(PortAddress,byte);
		return;
	};

    unsigned int error;
    DWORD BytesReturned;        
    unsigned char Buffer[3];
    unsigned short * pBuffer;
    pBuffer = (unsigned short *)&Buffer[0];
    *pBuffer = PortAddress;
    Buffer[2] = byte;

    error = DeviceIoControl(PortTalk_Handle,
                            IOCTL_WRITE_PORT_UCHAR,
                            &Buffer,
                            3,
                            NULL,
                            0,
                            &BytesReturned,
                            NULL);

    if (!error) 
		AfxMessageBox("Errore di comunicazione con il driver",MB_OK,0);
}

L'utilizzo della classe Φ molto semplice. Bisogna dichiarare una variabile di tipo Cport e assegnare il corretto valore all'indirizzo. A questo punto l'operazione di lettura Φ svolta attraverso il metodo leggi che aggiorna le proprietα value (valore decimale della variabile), e bit[i] (array contenente i singoli bit). L'operazione di scrittura avviene sul singolo bit attraverso il metodo setout(unsigned int value, unsigned char bitnum).
Per funzionare correttamente insieme alla calsse Cport Φ necessario includere nella cartella dell'eseguibile anche il file porttalk.sys (il vero driver).

Tutto cos∞ semplice? No.
Installare un servizio o un driver in Windows NT o 2000 o XP non Φ cosa da tutti! O meglio bisogna avere i diritti di amministratori. Questo vale solo per la prima volta. Basta allora far eseguire una volta il programma dall'amministratore e poi avete il controllo delle porte I/O.
In allegato trovate un progetto per il controllo della porta parallela equivalente a quello pubblicato su ioProgrammo n.57-58, ma funzionante con tutti i sistemi operativi.
Tip fornito dal Sig. F. Gobbi


Come sospendere l'esecuzione di un programma


A volte Φ necessario sospendere l'esecuzione di un'applicazione per un determinato periodo di tempo. La funzione "pausa" scritta in C+, ed oggetto di questo tip, fa proprio al caso nostro: sospende, infatti, l'esecuzione di un programma per un intervallo di tempo espresso in secondi. Di seguito viene riportato il listato corredato da un esempio di utilizzo. Tip fornito dal Sig. R.Pizzolante

#include 
#include 

/*funzione pausa*/
void pausa(double sec)
{
   clock_t t = (clock_t)(clock() + (CLOCKS_PER_SEC * sec));

   while (clock() < t)
      ;
   return;
}

/*Esempio di utilizzo :
stampa la stringa puntata da s con effetto "macchina da scrivere" */

int main(void)
{
   char *s = "Raffaele Pizzolante";

   for ( ; *s; s++) {
      cout << *s;
      pausa(1.0 / 5.0); /*1/5 di secondo*/
   }
   return 0;
}



Come monitorare i processi di Windows


Questo tip mostra come sia possibile monitorare i processi di Windows con un'applicazione C#. In particolare si possono ottenere dettagliate informazioni riguardanti l'ID del processo, la memoria utilizzata, il modulo principale, etc.; La classe "Process" del Namespace "System.Diagnostic" consente, inoltre, di inviare un segnale "Kill" ai processi attivi. Tip fornito dal Sig D.Camassa

 private void startMonitor()
{ while(true) { //Per l'aggiornamento dell'albero Φ necessario utilizzare un delegato ProcessTree.BeginInvoke(new UpdateTree(UpdateTreeDelegate)); //Refresh ogni 10 secondi thProcess.Join(5000); } } private void UpdateTreeDelegate() { ProcessTree.Nodes[0].Nodes.Clear(); ProcessTree.BeginUpdate(); //ottengo un array dei processi attivi process=System.Diagnostics.Process.GetProcesses(); //costruisco l'albero dei processi for(int i=0;i<process.Length;i++) { ProcessTree.Nodes[0].Nodes.Add(new TreeNode(process[i].ProcessName,1,1)); } ProcessTree.EndUpdate(); //Riseleziono l'ultimo nodo scelto prima del refresh ProcessTree.SelectedNode=ProcessTree.Nodes[0].Nodes[SelectedNodeIndex]; } private void ProcessTree_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e) { if(!(((TreeView)sender).SelectedNode.Text.Equals("Processi"))) { try { SelectedNodeIndex=ProcessTree.SelectedNode.Index; Process pr=Process.GetProcessById(process[((TreeView)sender). SelectedNode.Index].Id); StringBuilder sb=new StringBuilder(); //Ottengo tutte le informazioni sul processo selezionato sb.Append("Name:"+pr.ProcessName+"\n"); sb.Append("ID:"+pr.Id+"\n"); sb.Append("Prioritα:"+pr.BasePriority+"\n"); sb.Append("Handle:"+pr.Handle +"\n"); sb.Append("Modulo principale:"+pr.MainModule+"\n"); sb.Append("Dimensione della memoria privata:"+pr.PrivateMemorySize +"\n"); sb.Append("Avvio:"+pr.StartTime +"\n"); sb.Append("thread:"+pr.Threads+"\n"); sb.Append("Tempo utente di processore:"+pr.UserProcessorTime +"\n"); sb.Append("Memoria virtuale usata:"+pr.VirtualMemorySize+"\n"); InformationProcess.Text=sb.ToString(); } catch(Exception err) { InformationProcess.Text="Errore nella lettura delle informazioni: "+err.Message; } } } private void menuItem1_Click(object sender, System.EventArgs e) { //ATTENZIONE:Uccido il processo selezionato try { process[ProcessTree.SelectedNode.Index].Kill(); ProcessTree.SelectedNode=ProcessTree.Nodes[0].Nodes[0]; } catch(Exception er){MessageBox.Show("Errore: "+er.Message);} }